import LoggerManager from "@/log/LoggerManager.ts";
import type {EventCallbackFunction, EventData, EventType, EventTypeCallbackDataMap} from "@/event/index";

const log = LoggerManager.getLogger("EventDispatch");

interface ListenersValue<T extends EventType> {
    callback: EventCallbackFunction<T>,
    identity: string
}

/**
 * 全局事件总线, 事件派发处理类
 */
export default class EventDispatch {

    /**
     * 事件类型和事件对应的监听者处理函数列表映射
     */
    private readonly listenerMap: Map<EventType, ListenersValue<EventType>[]> = new Map<EventType, ListenersValue<EventType>[]>();
    /**
     * 单例模式
     */
    private static readonly instance = new EventDispatch();

    private constructor() {
        log.debug("EventDispatch initialized.")
    }

    /**
     * 获取事件调用类实例
     */
    public static getInstance(): EventDispatch {
        return this.instance;
    }

    /**
     * 发布系统消息事件
     */
    private publishSystemEvent(msg: string) {
        log.debug("Public system event: ", msg);
        const listenerList = this.listenerMap.get("system");
        if (listenerList) {
            const eventData: EventData<'system'> = {
                currentSubscribeEvent: Array.from(this.listenerMap.keys()),
                subscribeInfo: {},
                updateMessage: msg
            }
            this.listenerMap.forEach((value, key) => {
                eventData.subscribeInfo[key] = value.map(e => e.identity);
            })
            for (const temp of listenerList) {
                temp.callback(eventData, "EVENT_BUS")
            }
        }
    }

    /**
     * 添加事件监听器对象到监听列表
     * @param {EventType} eventType - 要监听订阅的事件类型
     * @param {function} callback - 事件触发的回调函数
     * @param {string} identity - 事件监听订阅者的身份标识
     */
    public addListener<T extends EventType>(eventType: T, callback: EventCallbackFunction<T>, identity: string) {
        log.debug(`Add listener to bus, listening event type [ ${eventType} ], listener identity [ {${identity} ].`)
        // 如果监听器列表中已经存在此事件类型, 判断当前需要监听此类型的监听者是否存在
        const listenerList = this.listenerMap.get(eventType);
        if (listenerList) {
            // 遍历监听器列表, 如果当前身份已经注册过监听器, 则返回, 如果没有注册过则添加
            const flag = listenerList.some(item => item.identity === identity);
            if (flag) {
                log.debug(`Current listener[ ${identity} ] has already been registered [ ${eventType}] event.`)
                return;
            } else {
                this.publishSystemEvent(`Listener [ ${identity} ] start listening [ ${eventType} ] event.`)
                listenerList.push({
                    callback: callback as EventCallbackFunction<keyof EventTypeCallbackDataMap>,
                    identity
                });
            }
        } else {
            this.publishSystemEvent(`Listener [ ${identity} ] start listening [ ${eventType} ] event, first subscribe.`)
            // 如果监听器列表中不存在此类型的事件, 则添加新的
            this.listenerMap.set(eventType, [{
                callback: callback as EventCallbackFunction<keyof EventTypeCallbackDataMap>,
                identity
            }]);
        }
    }

    /**
     * 移除事件监听器对象
     * @param eventType 需要移除监听的事件类型
     * @param identity 监听者身份标识
     */
    public removeListener(eventType: EventType, identity: string) {
        log.debug(`Listener [ ${identity} ] cancel listening [ ${eventType} ] event.`);
        const listenerList = this.listenerMap.get(eventType);
        if (listenerList) {
            const index = listenerList.findIndex(item => item.identity === identity);
            if (index !== -1) {
                log.debug(`Listener [ ${identity} ] cancel listening [ ${eventType} ] event.`)
                listenerList.splice(index, 1);
            }
        }
    }

    /**
     * 监听事件派发处理函数
     * @param {EventType} eventType - 事件类型
     * @param {object} data - 事件数据
     * @param {string} publisher - 事件的发布者
     * @param {function} ack - 事件处理完成回调函数
     */
    public eventDispatch(eventType: EventType, data: EventData<EventType>, publisher: string, ack: (success: boolean) => void) {
        log.debug(`publisher [ ${publisher} ] publish [ ${eventType} ] event, event data is:`, data);
        const listenerList = this.listenerMap.get(eventType);
        if (listenerList) {
            for (const temp of listenerList) {
                temp.callback(data, publisher, (success) => {
                    log.debug(`Listener [ ${temp.identity} ] handle event complete, status: ${success}.`)
                    ack(success);
                })
            }
        }
    }

}